﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utils.Input;

namespace Utils.StageManagement
{
    abstract public class Stage
    {
        public enum State
        {
            Inactive,
            TransitionIn,
            Active,
            TransitionOut,
            Dead
        }

        /*
         * Fields
         */
        Stage calledByStage = null;
        State stageState;
        bool inBackground = false;
        bool coveredByOther = false;
        float transitionIntroDuration = 0f;
        float transitionOutroDuration = 0f;
        float transitionIntroTimer = 0f;
        float transitionOutroTimer = 0f;

        List<AbstractInputProcessor> inputProcessors = new List<AbstractInputProcessor>();

        /*
         * Properties
         */
        public State StageState { get { return stageState; } }
        public Stage CalledByStage { set { calledByStage = value; } }
        public bool InBackground { get { return inBackground; } }
        public bool CoveredByOther { get { return coveredByOther; } }

        public List<AbstractInputProcessor> InputProcessors { get { return inputProcessors; } }

        static public event EventHandler<StageTransitionEvent> TransitionEvent;

        /*
         * Methods
         */
        public void StartTransition(Stage toStage, TransitionType type)
        {
            if (stageState == State.Active)
            {
                TransitionEvent(this, new StageTransitionEvent(toStage, type));

                if (type == TransitionType.PopUp || type == TransitionType.OnTop)
                {
                    toStage.CalledByStage = this;
                }

                if (type != TransitionType.PopUp)
                {
                    StartTransitionOutro();
                }
            }
        }

        public void StartTransitionIntro()
        {
            stageState = State.TransitionIn;
            transitionIntroTimer = 0f;
        }

        public void StartTransitionOutro()
        {
            stageState = State.TransitionOut;
            transitionOutroTimer = 0f;
        }

        public void StageReturn () 
        {
            if(calledByStage != null) 
            {
                StartTransition(calledByStage, TransitionType.Return);
                calledByStage = null;
            }
        }

        public void ToBackground()
        {
            inBackground = true;
            OnHide();
        }

        public void ToForeground()
        {
            inBackground = false;
            OnReveal();
        }

        public void Hide()
        {
            coveredByOther = true;
            OnHide();
        }

        public void Reveal()
        {
            coveredByOther = false;
            OnReveal();
        }

        virtual public void OnReveal() { }
        virtual public void OnHide() { }

        virtual public void UpdateByManager(float dTime)
        {
            if (!coveredByOther && !inBackground)
            {
                foreach (AbstractInputProcessor processor in InputProcessors)
                {
                    processor.Update(InputManager.DEFAULT);
                }
                Update(dTime);

                if (stageState == State.Inactive)
                {
                    StartTransitionIntro();
                }
                else if (stageState == State.TransitionIn)
                {
                    transitionIntroTimer += dTime;
                    if (transitionIntroTimer >= transitionIntroDuration)
                    {
                        stageState = State.Active;
                    }
                }
                else if (stageState == State.TransitionOut)
                {
                    transitionOutroTimer += dTime;
                    if (transitionOutroTimer >= transitionOutroDuration)
                    {
                        stageState = State.Dead;
                    }
                }
            }
        }

        public virtual bool IsActive()
        {
            return !coveredByOther && !inBackground && stageState == State.Active;
        }

        abstract public void Update(float dTime);

        abstract public void Draw(float dTime);

        virtual public void LoadContent() { }

        virtual public void UnloadContent() { }

        virtual public void Init() { }

        virtual public void Shutdown() { }
    }
}
